Skip to content

fix: use os.walk to build custom components on Python 3.10 and 3.11#6760

Open
anxkhn wants to merge 1 commit into
reflex-dev:mainfrom
anxkhn:patch-1
Open

fix: use os.walk to build custom components on Python 3.10 and 3.11#6760
anxkhn wants to merge 1 commit into
reflex-dev:mainfrom
anxkhn:patch-1

Conversation

@anxkhn

@anxkhn anxkhn commented Jul 14, 2026

Copy link
Copy Markdown
Contributor
### All Submissions:

- [x] Have you followed the guidelines stated in CONTRIBUTING.md file?
- [x] Have you checked to ensure there aren't any other open Pull Requests for the desired change?

### Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

### Changes To Core Features:

- [x] Have you added an explanation of what your changes do and why you'd like us to include them?
- [x] Have you written new tests for your core changes, as applicable?
- [x] Have you successfully ran tests with your changes locally?

## What

`reflex component build` crashes on Python 3.10 and 3.11 with:

AttributeError: 'PosixPath' object has no attribute 'walk'


`_make_pyi_files` in `reflex/custom_components/custom_components.py` generates the
`.pyi` stubs for a custom component by walking its source tree. That loop used
`pathlib.Path.walk()`, which was only added in Python 3.12. Since the project
supports `requires-python = ">=3.10,<4.0"`, every custom-component author on 3.10
or 3.11 hits a hard crash when building their component.

## Why / root cause

`Path.walk()` does not exist before Python 3.12, and there is no version guard or
fallback around the call. The sibling helper `_walk_files` in
`packages/reflex-base/src/reflex_base/utils/pyi_generator.py` already avoids
`Path.walk()` for exactly this reason (its comment notes it can only be used once
the floor is 3.12), so this second call site had regressed relative to the rest
of the codebase.

## Fix

Switch the walk to `os.walk()` (`os` is already imported in the module) and read
the directory basename via `Path(dir).name` for the `__pycache__` skip.
`PyiGenerator.scan_all` normalizes its targets with `Path(...)`, so the directory
strings that `os.walk` yields work unchanged. This mirrors the existing
`_walk_files` approach and keeps the behavior identical on 3.12+.

## Tests

Added `tests/units/custom_components/test_custom_components.py`. It deletes
`Path.walk` via `monkeypatch` to reproduce the 3.10/3.11 environment on any
interpreter (so the guard also holds on 3.12+, where a naive test would silently
pass), then asserts `_make_pyi_files` recurses into component directories,
skips `__pycache__`, and skips top-level dotdirs, without raising.

- Fails before the fix with the reported `AttributeError`; passes after.
- `uv run pytest tests/units` -> 6617 passed, 18 skipped (no regressions).
- `uv run ruff check .` / `uv run ruff format --check` clean;
  `uv run pyright reflex tests` -> 0 errors.

Towncrier / CHANGELOG fragment

Committed news/6714.bugfix.md (placeholder number 6714) to satisfy the
CHANGELOG CI gate (.github/workflows/changelog.yml), which requires a towncrier
fragment for any change under reflex/. Verified locally:
uv run towncrier check --config pyproject.toml --dir . --compare-with origin/main
-> exit 0.

ACTION AFTER OPENING THE PR: rename news/6714.bugfix.md to
news/<real-PR-number>.bugfix.md and amend/commit. 6714 was the plausible next
number at draft time (latest activity was #6711-#6713), but the true PR number
is only known once the PR exists. Note news/6710.bugfix.md already exists in the
tree, so if the real PR lands on a number already taken by another fragment,
pick the actual PR number regardless.

`reflex component build` generates .pyi files for the custom component by
walking its source tree in `_make_pyi_files`. That loop used
`pathlib.Path.walk()`, which was only added in Python 3.12, so on Python
3.10 and 3.11 (both supported by `requires-python = ">=3.10,<4.0"`) the
build crashed with `AttributeError: 'PosixPath' object has no attribute
'walk'`.

Switch the walk to `os.walk()`, mirroring the existing `_walk_files`
helper in reflex-base whose comment already notes `Path.walk()` cannot be
used until the floor is 3.12. `PyiGenerator.scan_all` accepts str or Path
targets, so the yielded directory strings work unchanged.

Signed-off-by: Anas Khan <83116240+anxkhn@users.noreply.github.com>
@anxkhn anxkhn requested a review from a team as a code owner July 14, 2026 07:47
@greptile-apps

greptile-apps Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR restores custom-component builds on Python 3.10 and 3.11. The main changes are:

  • Replaces Path.walk() with compatible os.walk() traversal.
  • Adds coverage for recursion and excluded directories without Path.walk.
  • Documents the fix in a Towncrier fragment.

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.
  • scan_all() normalizes the strings yielded by os.walk() into paths.
  • The test patches the same generator symbol loaded by the production function.

Important Files Changed

Filename Overview
reflex/custom_components/custom_components.py Uses os.walk() for supported Python versions while preserving path normalization and directory filtering.
tests/units/custom_components/test_custom_components.py Adds a focused test for traversal without Path.walk, recursion, and excluded directories.
news/6714.bugfix.md Documents the Python 3.10 and 3.11 build compatibility fix.

Reviews (1): Last reviewed commit: "fix: use os.walk to build custom compone..." | Re-trigger Greptile

@codspeed-hq

codspeed-hq Bot commented Jul 14, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 26 untouched benchmarks
⏩ 8 skipped benchmarks1


Comparing anxkhn:patch-1 (72a7764) with main (32cc257)

Open in CodSpeed

Footnotes

  1. 8 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant